#region Using Statements
using System;
using System.Threading;
using System.Collections.Generic;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.GamerServices;
#endregion

namespace SqEngine.Components
{
    public class GuideX : GameComponent
    {
        #region Message Classes
        /// <summary>
        /// Maintains variables passed in by users.
        /// </summary>
        private class MessageBase
        {
            public PlayerIndex Index;
            public string Title;
            public string Text;
            public AsyncCallback Callback;
            public object State;
        }

        private class Message : MessageBase
        {
            public IEnumerable<string> Options;
            public MessageBoxIcon Icon;
            public int Selection;
        }

        private class KeyboardRequest : MessageBase
        {
            public string DefaultText;
        }
        #endregion

        #region Private Members
        // Contains a queue of messages to display.
        private Queue<MessageBase> messages = new Queue<MessageBase> ();

        // The current message being displayed.
        private MessageBase currentMessage;

        // Whether or not the guide is being used by
        // a message. Provides redundant checking since
        // Guide.IsVisible is not always correct.
        private bool inUse = false;
        #endregion

        #region Constructor
        public GuideX (Game game)
            : base ( game )
        {
        }
        #endregion

        #region Show Messages
        /// <summary>
        /// Shows a message box via the Xbox or Windows Guide using a Queue structure (FIFO).
        /// Automatically manages based on Guide status.
        /// </summary>
        /// <param name="index">Player index the message is for.</param>
        /// <param name="title">The title of the message.</param>
        /// <param name="text">The text of the message.</param>
        /// <param name="options">Options for the user to select, up to 3 allowed.</param>
        /// <param name="selection">The default selection, 0 based index.</param>
        /// <param name="icon">The icon for the message box.</param>
        /// <param name="callback">The callback for when the user selects an option.</param>
        /// <param name="state">The object state to pass along.</param>
        public void ShowMessage (PlayerIndex index, string title, string text, IEnumerable<string> options,
            int selection, MessageBoxIcon icon, AsyncCallback callback, object state)
        {
            // Store the data in a message
            Message msg = new Message ();
            msg.Index = index;
            msg.Title = title;
            msg.Text = text;
            msg.Options = options;
            msg.Callback = callback;
            msg.State = state;
            msg.Icon = icon;
            msg.Selection = selection;

            // Add the message to the queue
            messages.Enqueue ( msg );

            if ( Guide.IsVisible || InUse )
                return;

            ShowNextMessage ();
        }

        public void ShowKeyboard (PlayerIndex index, string title, string text,
            string defaultText, AsyncCallback callback, object state)
        {
            // Store the data in a message
            KeyboardRequest msg = new KeyboardRequest ();
            msg.Index = index;
            msg.Title = title;
            msg.Text = text;
            msg.DefaultText = defaultText;
            msg.Callback = callback;
            msg.State = state;

            // Add the message to the queue
            messages.Enqueue ( msg );

            if ( Guide.IsVisible || InUse )
                return;

            ShowNextMessage ();
        }
        #endregion

        #region Updating & Getting Next Message
        private void ShowNextMessage ()
        {
            // There are no messages left,
            // so just return.
            if ( messages.Count <= 0 )
                return;

            // We are using the guide, so no other
            // messages should display!
            InUse = true;

            // Get the next message to display and
            // keep a copy of it at class level.
            currentMessage = messages.Dequeue ();

            // Do a specific task based on what kind
            // of message it is.
            if ( currentMessage is Message )
            {
                // Show a message box
                Message msg = currentMessage as Message;
                Guide.BeginShowMessageBox ( msg.Index, msg.Title, msg.Text,
                    msg.Options, msg.Selection, msg.Icon, MessageEnded, msg.State );
            }
            else if ( currentMessage is KeyboardRequest )
            {
                // Get keyboard input!
                KeyboardRequest kreq = currentMessage as KeyboardRequest;
                Guide.BeginShowKeyboardInput ( kreq.Index, kreq.Title, kreq.Text,
                    kreq.DefaultText, MessageEnded, kreq.State );
            }
        }

        private void MessageEnded (IAsyncResult result)
        {
            if ( currentMessage.Callback != null )
                currentMessage.Callback ( result );

            // Reset that we are not using the guide.
            // This is to provide a failsafe check against
            // the IsVisible state of the Guide.
            InUse = false;
        }

        public override void Update (GameTime gameTime)
        {
            base.Update ( gameTime );

            // Try to get the next message
            if ( messages.Count > 0 && !InUse && !Guide.IsVisible )
                ShowNextMessage ();
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets whether or not the Guide is in use.
        /// </summary>
        public bool InUse
        {
            get { return inUse; }
            private set { inUse = value; }
        }
        #endregion
    }
}
